/*
 *  JsonRpc-Cpp - JSON-RPC implementation.
 *  Copyright (C) 2008-2011 Sebastien Vincent <sebastien.vincent@cppextrem.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * \file jsonrpc_httpclient.h
 * \brief JSON-RPC HTTP client.
 * \author Brian Panneton
 */

#ifndef JSONRPC_HTTPCLIENT_H 
#define JSONRPC_HTTPCLIENT_H 

#include <iostream>
#include <curl/curl.h>

#include "jsonrpc_client.h"
#include "system.h" 

namespace Json
{
namespace Rpc
{
/**
 *�\class HttpClient
 * \brief JSON-RPC Http client.
 */
class HttpClient: public Client
{
public:
	/**
	 * \brief Constructor without address or port.
	 * \note Make sure you set them yourself.
	 */
	HttpClient();

	/**
	 * \brief Destructor.
	 */
	virtual ~HttpClient();

	/**
	 * \brief Constructor.
	 * \param address address URL
	 * \note Make sure you either set the port or include it
	 * in the string. If you do both, the one set by ChangePort()
	 * will override.
	 */
	HttpClient(const std::string& address);

	/**
	 * \brief Constructor.
	 * \param address remote network address
	 * \param port remote local port
	 * \note Port will override whatever is set in the address string.
	 */
	HttpClient(const std::string& address, uint16_t port);

	/**
	 * \brief Provide the option to change the address.
	 * \param address string containing the address (include the port here)
	 * \note ChangePort() will override the port added in address.
	 */
	void ChangeAddress(const std::string& address);

	/**
	 * \brief Provide the option to change the port, this will override the
	 * port set in ChangeAddress.
	 * \param port number
	 */
	void ChangePort(uint16_t port);

	/**
	 * \brief Receive last string received from server.
	 *
	 * Strings received from server are placed in a linked list (FIFO)
	 * which Recv pops from the front. 'data' will be null if there
	 * are not strings left.
	 * \param data if data is received it will put in this reference
	 * \return length of string
	 */
	ssize_t Recv(std::string& data);

	/**
	 * \brief Receives next string from server, will wait for data.
	 * \param data data received is put in this reference
	 * \return length of string
	 */
	ssize_t WaitRecv(std::string& data);

	/**
	 * \brief Receives next string from server, will wait for data for max
	 * timeout seconds.
	 * \param data data received is put in this reference
	 * \param timeout timeout for receive operation
	 * \return length of string
	 */
	ssize_t WaitRecv(std::string& data, unsigned int timeout);

	/**
	 * \brief Sets the size of the receive string list.
	 *
	 * If the list exceeds this size, a strings will be popped off of the
	 * front until it is of correct size; Default size: -1 == unlimited.
	 * \param size number of strings to keep in the list
	 */
	void SetRecvListSize(int size);

	/**
	 * \brief Gets the size of the receive string list.
	 * \return size of the receive string list (-1 == unlimited)
	 */
	int GetRecvListSize();

	/**
	 * \brief Send data.
	 * \param data data to send
	 * \return 0 if successful or libcurl error code if error
	 */
	ssize_t Send(const std::string& data);

	/**
	 * \brief Connect to the remote machine.
	 * \return true if success, false otherwise
	 * \note This method is not needed in HTTP and always returns false.
	 */
	bool Connect();

	/**
	 * \brief Close socket.
	 * \note This method is useless in HTTP and does nothing.
	 */
	void Close();

	/**
	 * \brief Get socket descriptor.
	 * \return socket descriptor.
	 * \note This method is useless in HTTP and always returns -1.
	 */
	int GetSocket() const;

private:
	/**
	 * \brief Receive data from the network.
	 *
	 * This is a static function that is the callback when receiving data
	 * from the network. It calls Recv providing it with 'this'.
	 * \param buffer Non-null terminated data that it received back
	 * \param size size_t count of how many nmemb make up buffer
	 * \param nmemb size_t multiply by size to get full size of buffer
	 * \param f pointer to user data (in our case it is out class instance)
	 * \return the size of the data received
	 */
	static size_t StaticRecv(void *buffer, size_t size, size_t nmemb, void*f);

	/**
	 * \brief Stores the last received json string from the server
	 * This allows the last json string that the server sent
	 * to the static callback to be stored within the class.
	 * The next json string from the server will overwrite this.
	 * \param data the string represention of the received json
	 * \note Any server errors will also be stored here.
	 */
	void StoreRecv(const std::string& data);

	/**
	 * \brief Initializes the class instance
	 */
	void Initialize();

	/**
	 * \brief CURL handle.
	 */
	CURL *m_curlHandle;

	/**
	 * \brief List of CURL headers.
	 */
	struct curl_slist *m_headers;

	/**
	 * \brief List of received JSON-RPC response.
	 */
	std::list<std::string> m_lastReceivedList;

	/**
	 * \brief Size of the m_lastReceivedList list.
	 */
	int m_lastReceivedListSize;

	/**
	 * \brief Mutex to protected m_lastReceivedList.
	 */
	system_util::Mutex m_mutex;
};
} /* namespace Rpc */
} /* namespace Json */

#endif /* JSONRPC_HTTPCLIENT_H */

